package com.anttribe.components.db.sql;

import java.util.Set;

import com.anttribe.components.db.sql.statement.dialect.StatementDialect;
import org.apache.commons.collections4.CollectionUtils;

import com.anttribe.components.db.sql.exception.SqlStatementException;
import com.anttribe.components.db.sql.stereotype.SqlStatement;
import com.anttribe.components.db.metadata.Dialect;
import com.anttribe.components.infra.lang.reflect.ClassScannerUtils;
import com.anttribe.components.infra.lang.reflect.ReflectUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @author zhaoyong
 * @date 2022/11/21 0021
 */
@Slf4j
public class SqlStatementFactory {

    /**
     * SqlStatement的实现子类的包名
     */
    private static final String IMPL_SQL_STATEMENT_PACKAGE_NAME =
        ReflectUtils.getPackageName(SqlStatementFactory.class);

    /**
     * <私有>
     */
    private SqlStatementFactory() {}

    public static SqlStatementFactory me() {
        return SqlStatementFactory.SqlStatementFactoryInstance.INSTANCE;
    }

    /**
     * 创建SqlStatement
     *
     * @param sqlOperation
     *            SqlOperation
     * @param dialect
     *            Dialect
     * @return SqlStatement
     */
    public SqlStatement createSqlStatement(SqlOperation sqlOperation, Dialect dialect) {
        if (null == sqlOperation || null == dialect) {
            throw new SqlStatementException("sqlOperation or dialect must not be null");
        }

        Class<? extends SqlStatement> sqlStatementClass = sqlOperation.getSqlStatementClazz();
        // 获取sqlStatement子类
        Set<Class<?>> subSqlStatementClasses = ClassScannerUtils.scanSubClasses(sqlStatementClass, (aClass) -> {
            if (aClass.isAnnotationPresent(
                StatementDialect.class)
                && dialect.equals(aClass.getAnnotation(StatementDialect.class).dialect())) {
                return true;
            }
            return false;
        }, IMPL_SQL_STATEMENT_PACKAGE_NAME);

        Class<?> sqlStatementClazz = sqlStatementClass;
        if (!CollectionUtils.isEmpty(subSqlStatementClasses)) {
            sqlStatementClazz = subSqlStatementClasses.stream().findFirst().get();
        }

        try {
            return (SqlStatement)sqlStatementClazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new SqlStatementException(e);
        }
    }

    private static class SqlStatementFactoryInstance {

        public static final SqlStatementFactory INSTANCE = new SqlStatementFactory();

    }

}
